package com.brillilab.common.utils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
import org.springframework.util.Assert;

/**
 * 反射的Utils函数集合 扩展自spring BeanUtils
 * 
 * @author LJ
 */
public class MyBeanUtils extends BeanUtils {

	private static Objenesis objenesis = new ObjenesisStd(true);
	/**
	 * 从一个对象中拷贝属性值，该属性在两个对象中名称相同且类型也相同
	 *
	 * @param ori
	 *            被拷贝的对象
	 * @param cls
	 *            拷贝对象的类型
	 * @return 拷贝对象的一个实例对象
	 */
	public static <T> T copyBean(Object ori, Class<T> cls) {
		if(ori==null) return null;
		T obj = (T) objenesis.newInstance(cls);
		copyProperties(ori, obj);
		return obj;
	}
	
	/**
	 * 只拷贝值不为NULL的属性值
	 *
	 * @param nullProperties
	 *            可以为空的属性名称，这些属性为null,也要进行拷贝
	 * @param source 源对象
	 * @param target 目标对象
	 * @throws BeansException
	 */
	public static void copyPropertiesNotNull(Object source, Object target, String... nullProperties)
			throws BeansException {
		Assert.notNull(source, "源对象不能为空。");
		Assert.notNull(target, "目标对象不能为空。");
		Class<?> actualEditable = target.getClass();
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		for (PropertyDescriptor targetPd : targetPds) {

			if (targetPd.getWriteMethod() != null) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null && sourcePd.getReadMethod() != null) {
					try {
						Method readMethod = sourcePd.getReadMethod();
						if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
							readMethod.setAccessible(true);
						}
						Object value = readMethod.invoke(source);
						// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时格式转换等等
						if (nullProperties != null) {
							String propName = targetPd.getName();
							List<String> nullPropList = Arrays.asList(nullProperties);
							if (nullPropList.contains(propName)) {
								copyValue(targetPd, target, value);
							} else {
								if (value != null) {
									copyValue(targetPd, target, value);
								}
							}
						} else {
							if (value != null) {
								copyValue(targetPd, target, value);
							}
						}
					} catch (Throwable ex) {
						throw new FatalBeanException("无法将属性从源复制到目标", ex);
					}
				}
			}
		}
	}

	private static void copyValue(PropertyDescriptor targetPd, Object target, Object value) {
		Method writeMethod = targetPd.getWriteMethod();
		if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
			writeMethod.setAccessible(true);
		}
		try {
			writeMethod.invoke(target, value);
		} catch (Throwable ex) {
			throw new FatalBeanException("无法将属性从源复制到目标", ex);
		}

	}

	

}
